//@author A0097978X
package flexiCommands;

import storage.TimeRange;
import storage.Time;
import storage.DateRange;
import storage.Date;

import java.lang.StringBuilder;
import java.lang.Integer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Calendar;

public class ParseAddInfo extends ParseInfo{
	
	private static final String REGEX_DATE = "([0-3]?\\d)((th|nd|st)|(([/])((\\d+)?\\d)))+";
	private static final String REGEX_MONTHS = "(?i)(January|Jan|February|Feb|March|Mar|April|Apr|May|June|Jun|July|Jul|August|Aug|September|Sept|Sep|October|Oct|November|Nov|December|Dec)";
	private static final String REGEX_DAYS_OF_WEEK = "(?i)(Monday|Mon|Tuesday|Tues|Tue|Wednesday|Weds|Wed|Thursday|Thurs|Thur|Friday|Fri|Saturday|Sat|Sunday|Sun)";
	private static final String REGEX_TIME = "((\\d?\\d)+((am|pm)|(:?\\d)?(am|pm)?))+";
	private static final String BY_SPACE_DASH = "(\\s)|[-]";
	private static final String BY_NUMBER_SYMBOL_WORD = "((\\d)+)((\\s)+)|((\\D)+)|((\\d)+)([/]*)([-]*)";
	private static final String YEAR_AFTER_MONTH = "([1-2][0-9])?(\\d\\d)?";

	private static final String ERROR_MESSAGE_TOO_FEW_ARGUMENT = "Too little info, please try again!";
	private static final String ERROR_MESSAGE_INVALID_MONTH = "Month is invalid, please try again!";

	private static final int YEAR_TYPE = 100;
	private static final int MONTH_TYPE = 31;
	private static final int DESCRIPTION_TYPE = 1;
	private static final int DATE_RANGE_TYPE = 2;
	private static final int TIME_RANGE_TYPE = 3;
	private static final int DATE_RELATIVE_TYPE = 4;
	private static final int TIME_RELATIVE_TYPE = 5;
	private static final int COMMAND = 0;

	private static final int UNPROCESSED = 0;
	private static final int TEMP_STATUS = 6;
	private static final int WORD_BEFORE = 1;
	private static final int WORD_2_PLACES_BEFORE = 2;
	private static final int TWO_WORDS = 2;
	private static final int NEXT_WORD = 1;
	private static final int FOLLOWING_WORD = 2;
	private static final int DATE_MONTH_YEAR_COMPLETED = 3;
	private static final int OCT_NOV_DEC = 10;
	private static final int INVALID_MONTH = 0;
	private static final int SINGLE_DIGIT = 1;
	private static final int DOUBLE_DIGIT = 2;
	private static final int TRIPLE_DIGIT = 3;
	private static final int HRS_1200 = 1200;
	private static final int AM_12 = 0;
	private static final int PM_12 = 12;
	private static final int NEXT_YEAR = 1;
	private static final int NEXT_MONTH = 1;
	private static final int NEXT_WEEK = 7;
	private static final int NEXT_DAY = 1;
	private static final int CURRENT = 0;
	private static final int TWO_DATES = 2;
	private static final int FIRST_DATE = 0;
	private static final int SECOND_DATE = 1;
	private static final int TWO_TIMES = 2;
	private static final int FIRST_TIME = 0;
	private static final int SECOND_TIME = 1;
	private static final int ONLY_ONE_DATE = 8;
	private static final int TWO_ARGUMENTS = 2;
	private static final int JUST_BEFORE_1PM = 1259;
	private static final int STARTING_FROM_12PM = 1159;
	
	private static final String AM = "am";
	private static final String PM = "pm";
	private static final String ND_POSTFIX = "nd";
	private static final String ST_POSTFIX = "st";
	private static final String TH_POSTFIX = "th";
	private static final String DIGIT_0 = "0";
	private static final String DIGIT_00 = "00";
	private static final String DIGIT_0000 = "0000";
	private static final String DIGIT_20 = "20";
	private static final String DIGIT_200 = "200";
	private static final String YEAR_2013 = "2013";
	private static final String YEAR_2014 = "2014";
	private static final String BLANK_SPACE = "";
	private static final String SLASH = "/";
	private static final String COLON = ":";
	private static final String ONE_SPACE = " ";
	private static final String DASH = "-";
	private static final String AUGUST_STRING = "AUGUST";
	private static final String TODAY_STRING = "TODAY";
	private static final String MIDNIGHT_STRING = "MIDNIGHT";
	private static final String NOON_STRING = "NOON";
	private static final String DAY_TYPES = "DAY";
	private static final String TO_STRING = "TO";
	private static final String IN_STRING = "IN";
	private static final String THIS_STRING = "THIS";
	private static final String MONTH_TYPES = "MONTH";
	private static final String NEXT_STRING = "NEXT";
	private static final String YEAR_TYPES = "(YEAR|YR)";
	private static final String WEEK_TYPES = "(WEEK|WK)";
	private static final String SUNDAY_TYPES = "(SUNDAY|SUN)";
	private static final String SATURDAY_TYPES = "(SATURDAY|SAT}";
	private static final String FRIDAY_TYPES = "(FRIDAY|FRI)";
	private static final String THURSDAY_TYPES = "(THURSDAY|THURS|THUR)";
	private static final String WEDNESDAY_TYPES = "(WEDNESDAY|WEDS|WED)";
	private static final String TUESDAY_TYPES = "(TUESDAY|TUES|TUE)";
	private static final String MONDAY_TYPES = "(MONDAY|MON)";
	private static final String HOURS_TYPES = "(HOURS|HOUR|HRS|HR)";
	private static final String MINUTES_TYPES = "(MINUTES|MINUTE|MINS|MIN)";
	private static final String TOMORROW_TMW_TMR_TML = "(TOMORROW|TMW|TMR|TML)";
	private static final String TODAY_TDY = "(TODAY|TDY)";
	private static final String TOMORROW_TMW_TMR_TML_TODAY_TDY = "(TOMORROW|TMW|TMR|TML|TODAY|TDY)";
	private static final String NOON_MIDNIGHT = "(NOON|MIDNIGHT)";
	private static final String MONTH_WEEK_WK_YEAR_YR = "(MONTH|WEEK|WK|YEAR|YR)";
	private static final String THIS_AND_NEXT = "(THIS|NEXT)";
	
	private String[] infoToParse;
	private Command processedCmd;
	private int[] typeDeterminer;

	private Pattern stringPatternDate = Pattern.compile(REGEX_DATE, Pattern.CASE_INSENSITIVE);
	private Pattern stringPatternTime = Pattern.compile(REGEX_TIME, Pattern.CASE_INSENSITIVE);
	private Matcher stringMatcherDate;
	private Matcher stringMatcherTime;

	public ParseAddInfo(String[] userInfo) throws Exception{
		super(userInfo);

		if(userInfo.length < TWO_ARGUMENTS)
			throw new Exception(ERROR_MESSAGE_TOO_FEW_ARGUMENT);
	}

	@Override
	public Command execute() throws NumberFormatException, Exception {
		infoToParse = super.infoString;
		processedCmd = super.cmdObj;

		processedCmd.setCommandType(infoToParse[COMMAND]);
				
		typeDeterminer = new int[infoToParse.length];

//======================================================================//
//====================== Flexible Arguments Type =======================//
//======================================================================//
		
		for(int i=1; i<infoToParse.length; i++) {
			if(typeDeterminer[i] == UNPROCESSED) {

				//==== Characters (Alphabets) ====//

				if(isAlpha(infoToParse[i])) {
		
					if(isMonth(infoToParse[i])) {
						typeDeterminer[i] = DATE_RANGE_TYPE; 
						if(i<infoToParse.length-1 && infoToParse[i+NEXT_WORD].matches(YEAR_AFTER_MONTH))
							typeDeterminer[i+NEXT_WORD] = DATE_RANGE_TYPE;	
						if(typeDeterminer[i-WORD_BEFORE] != DATE_RANGE_TYPE)
							typeDeterminer[i-WORD_BEFORE] = DATE_RANGE_TYPE; }
					
					//==== Check before "To" =====//
	
					else if(infoToParse[i].toUpperCase().equals(TO_STRING)) {
						if(typeDeterminer[i-WORD_BEFORE] == DATE_RANGE_TYPE || typeDeterminer[i-WORD_BEFORE] == TIME_RANGE_TYPE) {
							typeDeterminer[i] = TEMP_STATUS;

							stringMatcherTime = stringPatternTime.matcher(infoToParse[i+NEXT_WORD]);		//check after to
							if(stringMatcherTime.matches()) {
								if(infoToParse[i+NEXT_WORD].contains(PM)) 
										infoToParse[i-WORD_BEFORE] = new String(infoToParse[i-WORD_BEFORE] + PM); 
								else if(infoToParse[i+NEXT_WORD].contains(AM)) 
										infoToParse[i-WORD_BEFORE] = new String(infoToParse[i-WORD_BEFORE] + AM); 
								typeDeterminer[i-WORD_BEFORE] = typeDeterminer[i] = TIME_RANGE_TYPE; }
							else	
								typeDeterminer[i-WORD_BEFORE] = typeDeterminer[i] = DATE_RANGE_TYPE;		}
						else
							typeDeterminer[i] = DESCRIPTION_TYPE;	}										//end of "to"

					//==== Relative Date ====//
	
					else if(infoToParse[i].toUpperCase().matches(THIS_AND_NEXT)) {
						if(isDayOfWeek(infoToParse[i+NEXT_WORD]) || infoToParse[i+NEXT_WORD].toUpperCase().matches(MONTH_WEEK_WK_YEAR_YR)) 
							typeDeterminer[i+NEXT_WORD] = typeDeterminer[i] = DATE_RELATIVE_TYPE; 
						else
							typeDeterminer[i] = DESCRIPTION_TYPE;	}
	
					else if(infoToParse[i].toUpperCase().matches(TOMORROW_TMW_TMR_TML_TODAY_TDY)) {
						typeDeterminer[i] = DATE_RELATIVE_TYPE; 
					}

					//==== Relative Time====//
	
					else if(infoToParse[i].toUpperCase().matches(IN_STRING) && isInteger(infoToParse[i+NEXT_WORD])) {
						typeDeterminer[i] = typeDeterminer[i+NEXT_WORD] = typeDeterminer[i+FOLLOWING_WORD] = TIME_RELATIVE_TYPE;
					}	
					
					else if(infoToParse[i].toUpperCase().matches(NOON_MIDNIGHT)) {
						typeDeterminer[i] = TIME_RELATIVE_TYPE;
					}	
	
					else typeDeterminer[i] = DESCRIPTION_TYPE;		}
				
				//==== Integer (Numerical Digits) ====//

				else {				
					stringMatcherDate = stringPatternDate.matcher(infoToParse[i]);	
					stringMatcherTime = stringPatternTime.matcher(infoToParse[i]);	

					if(infoToParse[i].contains(DASH) && infoToParse[i].contains(PM)) {
						int idx = infoToParse[i].indexOf(DASH);
						infoToParse[i] = new String(infoToParse[i].substring(0, idx)+PM+infoToParse[i].substring(idx,infoToParse[i].length()));
						typeDeterminer[i] = TIME_RANGE_TYPE;}
					else if(!stringMatcherDate.matches() && !stringMatcherTime.matches())	
						typeDeterminer[i] = DESCRIPTION_TYPE;
					else if (stringMatcherDate.matches() && !stringMatcherTime.matches())
						typeDeterminer[i] = DATE_RANGE_TYPE;
					else if (!stringMatcherDate.matches() && stringMatcherTime.matches())
						typeDeterminer[i] = TIME_RANGE_TYPE;
				}
			}
		}						
//======================================================================//
//======================================================================//

		if(checkTypeDeterminer()) {									
			//process and add Date(s) into Command Object, if it is found
			for(int d=1; d<typeDeterminer.length; d++) {
				if(typeDeterminer[d] == DATE_RANGE_TYPE) {
					setDateRange();
					break;			
				}
			}

			//process and add Time(s) into Command Object, if it is found
			for(int t=1; t<typeDeterminer.length; t++) {
				if(typeDeterminer[t] == TIME_RANGE_TYPE) {
					setTimeRange();
					break;			
				}
			}

			//process and add Relative Date(s) into Command Object, if it is found
			for(int t=1; t<typeDeterminer.length; t++) {
				if(typeDeterminer[t] == DATE_RELATIVE_TYPE) {
					setDateRelative();
					break;			
				}
			}

			//process and add Relative Time(s) into Command Object, if it is found
			for(int t=1; t<typeDeterminer.length; t++) {
				if(typeDeterminer[t] == TIME_RELATIVE_TYPE) {
					setTimeRelative();
					break;			
				}
			}

			//process and add Description into Command Object
			setDescription();
		}

		return processedCmd;
	}


	private boolean checkTypeDeterminer() {
		for(int i=1; i<typeDeterminer.length; i++) {
			if(typeDeterminer[i] == UNPROCESSED)
				return false;
		}

		return true;
	}


//===================================================================================//
//============================ Methods for Parsing Fields ===========================//
//===================================================================================//

	private void setDescription() {
		StringBuilder description = new StringBuilder();
		for(int j=1; j<typeDeterminer.length; j++) {
			if(typeDeterminer[j] == DESCRIPTION_TYPE) {
				description.append(infoToParse[j] + ONE_SPACE);
			}
		}

		processedCmd.setDescription(description.toString().trim());								// sets Description (String) in Command Obj
	}

	private void setDateRange() throws NumberFormatException, Exception {
		StringBuilder dateRange = new StringBuilder();
		for(int j=1; j<typeDeterminer.length; j++) {
			if(typeDeterminer[j] == DATE_RANGE_TYPE) {
				dateRange.append(infoToParse[j] + ONE_SPACE);
			}

			if(infoToParse[j].toUpperCase().equals(TO_STRING) && typeDeterminer[j+NEXT_WORD] == DATE_RANGE_TYPE) {
				dateRange.append(infoToParse[j] + ONE_SPACE);
				typeDeterminer[j-WORD_BEFORE] = typeDeterminer[j] = DATE_RANGE_TYPE;
				StringBuilder tempDateRange = new StringBuilder();
				if(!isMonth(infoToParse[j-WORD_BEFORE]))
					tempDateRange.append(infoToParse[j-WORD_BEFORE] + ONE_SPACE + dateRange.toString());
				else 
					tempDateRange.append(infoToParse[j-WORD_2_PLACES_BEFORE] + ONE_SPACE + dateRange.toString().substring(dateRange.toString().indexOf("to"), dateRange.toString().length()));
				dateRange = tempDateRange;
			}
		}
		
		processedCmd.setDateRange(generateDateRange(dateRange.toString().trim()));				// sets RangeDate (DateRange) in Command Obj
	}

	private void setTimeRange() throws NumberFormatException, Exception {
		StringBuilder timeRange = new StringBuilder();
		for(int j=1; j<typeDeterminer.length; j++) {
			if(typeDeterminer[j] == TIME_RANGE_TYPE) 
				timeRange.append(infoToParse[j] + ONE_SPACE);

			if(infoToParse[j].toUpperCase().equals(TO_STRING) && typeDeterminer[j-WORD_BEFORE] == TIME_RANGE_TYPE) {
				timeRange.append(infoToParse[j] + ONE_SPACE);
				typeDeterminer[j] = TIME_RANGE_TYPE;
			}
		}
		
		processedCmd.setTimeRange(generateTimeRange(timeRange.toString().trim()));				// sets RangeTime (TimeRange) in Command Obj
	}
	
	private void setDateRelative() throws NumberFormatException, Exception {
		StringBuilder dateRelative = new StringBuilder();
		for(int j=1; j<typeDeterminer.length; j++) {
			if(typeDeterminer[j] == DATE_RELATIVE_TYPE) {
				dateRelative.append(infoToParse[j] + ONE_SPACE);
			}

			if(infoToParse[j].toUpperCase().equals(TO_STRING) && typeDeterminer[j+NEXT_WORD] == DATE_RELATIVE_TYPE)
				typeDeterminer[j-WORD_BEFORE] = typeDeterminer[j] = DATE_RANGE_TYPE;
		}
		
		processedCmd.setDateRange(generateRelativeDateRange(dateRelative.toString().trim()));	// sets RangeDate (DateRange) in Command Obj
	}
	
	private void setTimeRelative() throws NumberFormatException, Exception {
		StringBuilder timeRelative = new StringBuilder();
		for(int j=1; j<typeDeterminer.length; j++) {
			if(typeDeterminer[j] == TIME_RELATIVE_TYPE) {
				timeRelative.append(infoToParse[j] + ONE_SPACE);
			}
		}
		processedCmd.setTimeRange(generateRelativeTimeRange(timeRelative.toString().trim()));	// sets RangeTime (TimeRange) in Command Obj
	}

//=========================================================================//
//============================ Refining Details ===========================//
//=========================================================================//
	
	private DateRange generateRelativeDateRange(String dateRelative) throws NumberFormatException, Exception {
		String[] dayParts = dateRelative.split(BY_SPACE_DASH);

		Calendar cal = Calendar.getInstance();
		java.util.Date currentDate = cal.getTime();
		Date firstDate = new Date();
		Date secondDate = new Date();
		boolean isFirstDateFilled = false;
		boolean isSecondDateFilled = false;
		
		int day = cal.get(Calendar.DAY_OF_MONTH);
	    int month = cal.get(Calendar.MONTH) + 1;
	    
	    int count = 0;
	    while(count < dayParts.length) {

	    	if(dayParts[count].toUpperCase().matches(TODAY_TDY)) {} //do nothing
		
	    	if(dayParts[count].toUpperCase().matches(TOMORROW_TMW_TMR_TML)) {
	    		cal.add(Calendar.DATE, NEXT_DAY);

	    		if(cal.get(Calendar.DAY_OF_MONTH) < day && cal.get(Calendar.MONTH) > month) 		cal.add(Calendar.MONTH, NEXT_MONTH);
	    	}
		
	    	if(dayParts[count].toUpperCase().equals(THIS_STRING) && count < dayParts.length-1) {
	    			 if(dayParts[count+NEXT_WORD].toUpperCase().matches(MONDAY_TYPES)) 				cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(TUESDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY);
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(WEDNESDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(THURSDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(FRIDAY_TYPES)) 				cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(SATURDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(SUNDAY_TYPES)) 				cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
	    		
	    		count++;
	    	}
		
	    	if(dayParts[count].toUpperCase().equals(NEXT_STRING) && count < dayParts.length-1) {
	    			 if(dayParts[count+NEXT_WORD].toUpperCase().matches(MONTH_TYPES)) 				cal.add(Calendar.MONTH, NEXT_MONTH);		
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(WEEK_TYPES)) 				cal.add(Calendar.DATE, NEXT_WEEK);		
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(YEAR_TYPES)) 				cal.add(Calendar.YEAR, NEXT_YEAR);		
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(DAY_TYPES))					cal.add(Calendar.DATE, NEXT_DAY);	

	    		else {
	    				 if(dayParts[count+NEXT_WORD].toUpperCase().matches(MONDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
	    			else if(dayParts[count+NEXT_WORD].toUpperCase().matches(TUESDAY_TYPES)) 		cal.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY);
	    			else if(dayParts[count+NEXT_WORD].toUpperCase().matches(WEDNESDAY_TYPES)) 		cal.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
	    			else if(dayParts[count+NEXT_WORD].toUpperCase().matches(THURSDAY_TYPES)) 		cal.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
	    			else if(dayParts[count+NEXT_WORD].toUpperCase().matches(FRIDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
	    			else if(dayParts[count+NEXT_WORD].toUpperCase().matches(SATURDAY_TYPES)) 		cal.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
	    			else if(dayParts[count+NEXT_WORD].toUpperCase().matches(SUNDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
			
	    			//next week
	    			cal.add(Calendar.DATE, NEXT_WEEK);		}

	    		count++;	
	    	}
		
	    	if(cal.get(Calendar.DAY_OF_MONTH) < day && cal.get(Calendar.MONTH) > month)				cal.add(Calendar.MONTH, NEXT_MONTH);
	    	
	    	if(isFirstDateFilled == false) {
	    		firstDate.setDay(cal.get(Calendar.DAY_OF_MONTH));
	    		firstDate.setMonth(cal.get(Calendar.MONTH) + 1);
	    		firstDate.setYear(cal.get(Calendar.YEAR));
	    		cal.setTime(currentDate);

	    		isFirstDateFilled = true;					}
	    	
	    	else if(isSecondDateFilled == false) {
	    		secondDate.setDay(cal.get(Calendar.DAY_OF_MONTH));
	    		secondDate.setMonth(cal.get(Calendar.MONTH) + 1);
	    		secondDate.setYear(cal.get(Calendar.YEAR));

	    		isSecondDateFilled = true;					}
	    	
	    	count++;
	    }
		
		if(processedCmd.isEndDateEmpty()) {
			 	  if(isFirstDateFilled == true && isSecondDateFilled == true) return new DateRange(firstDate, secondDate);
			 else if(isFirstDateFilled == true && isSecondDateFilled == false) return new DateRange(new Date(), firstDate);
			 else return new DateRange(new Date(), new Date()); }
		else {
			Date existingDate = processedCmd.getEndDate();
			if(existingDate.getYear() >= firstDate.getYear() && existingDate.getMonth() >= firstDate.getMonth() && existingDate.getDay() >= firstDate.getDay() )	return new DateRange(firstDate, existingDate); 
			else return new DateRange(existingDate, firstDate);}
		
	}

	private TimeRange generateRelativeTimeRange(String timeRelative) throws NumberFormatException, Exception {
		String[] timeParts = timeRelative.split(BY_SPACE_DASH);

		Calendar cal = Calendar.getInstance();
		Time firstTime = new Time();
		
		if(timeParts.length > TWO_WORDS) {
			if(timeParts[FOLLOWING_WORD].toUpperCase().matches(MINUTES_TYPES))
				cal.add(Calendar.MINUTE, Integer.parseInt(timeParts[NEXT_WORD]));
	    
			else if(timeParts[FOLLOWING_WORD].toUpperCase().matches(HOURS_TYPES)) 
				cal.add(Calendar.HOUR_OF_DAY, Integer.parseInt(timeParts[NEXT_WORD]));		}

		else {
	    	if(timeParts[CURRENT].toUpperCase().equals(NOON_STRING)) {
	    		cal.set(Calendar.HOUR_OF_DAY, PM_12);
	    		cal.set(Calendar.MINUTE, 0);			}
	    
	   		else if(timeParts[CURRENT].toUpperCase().equals(MIDNIGHT_STRING)) {
	    		cal.set(Calendar.HOUR_OF_DAY, AM_12);
	    		cal.set(Calendar.MINUTE, 0);			}
	   	}

	    firstTime.setHour(cal.get(Calendar.HOUR_OF_DAY));
	    firstTime.setMinute(cal.get(Calendar.MINUTE));
	    
	    //checks if End Date is empty
	     if(processedCmd.isEndDateEmpty()) 
	    	processedCmd.setDateRange(new DateRange(new Date(), Clock.getCurrentDate()));


		return new TimeRange(new Time(), firstTime);
	}
	
	private DateRange generateDateRange(String dateRange) throws NumberFormatException, Exception {
		StringBuilder dateRange_newform = new StringBuilder();
		Pattern stringPattern = Pattern.compile(BY_NUMBER_SYMBOL_WORD, Pattern.CASE_INSENSITIVE);
		Matcher stringMatcher = stringPattern.matcher(dateRange);

		boolean isToFound = false;
		String beforeTo = new String();

		int date_count = 1;
		while(stringMatcher.find()) {
			String current = stringMatcher.group().trim();
			if(current.toUpperCase().matches(TO_STRING)) {
				isToFound = true;
				beforeTo = dateRange_newform.toString(); 	//temp store all that has been processed before visiting to
				dateRange_newform = new StringBuilder();
				date_count = 1;
			}
			
			if(current.contains(DASH)) {
				isToFound = true;
				beforeTo = current.substring(0, current.length()-1);
				if(Integer.parseInt(beforeTo) < 10)
					beforeTo = new String(DIGIT_0 + beforeTo);
				dateRange_newform = new StringBuilder();
				date_count = 1;
			}

			if(current.contains(SLASH)) {
				current = current.substring(0, current.length()-1); }

			if((current.contains(TH_POSTFIX) || current.contains(ST_POSTFIX) || current.contains(ND_POSTFIX)) && !current.toUpperCase().equals(AUGUST_STRING)) {
				current = current.substring(3, current.length()); }

	//==== Integer (Numerical Digits) ====//

			if(isInteger(current) && (Integer.parseInt(current) < MONTH_TYPE && (date_count < DATE_MONTH_YEAR_COMPLETED))) {	
		 		date_count++;			
				if(Integer.parseInt(current) < 10) {
					if(current.length() > 1) dateRange_newform.append(current); 		//ie. 01
					else dateRange_newform.append(DIGIT_0 + current);	} 				//ie. _1
				else dateRange_newform.append(current); 	}

		    else if (date_count == DATE_MONTH_YEAR_COMPLETED) {
				if(Integer.parseInt(current) < YEAR_TYPE && Integer.parseInt(current) > 9)
					dateRange_newform.append(DIGIT_20 + current);
				else if(Integer.parseInt(current) < 10)
					dateRange_newform.append(DIGIT_200 + current);
				else 
					dateRange_newform.append(current); 	    
				date_count++;	}

	//==== Characters (Alphabets) ====//

		    else if(!current.toUpperCase().equals(TO_STRING)) {
				String[] temp = current.split(BY_SPACE_DASH);
				current = temp[0];
		    	int month = getMonth(current);
		    	if(month < OCT_NOV_DEC && month != INVALID_MONTH) {
		    		date_count++;
		    		dateRange_newform.append(DIGIT_0 + month);	}
		    	else {
		    		if(month != INVALID_MONTH) {
		    			date_count++;
		    			dateRange_newform.append(month);	}	
		    		else if(!current.toUpperCase().equals(TODAY_STRING))
		    			throw new Exception(ERROR_MESSAGE_INVALID_MONTH);	}
		    }

		    if(date_count > DATE_MONTH_YEAR_COMPLETED) {
		    	dateRange_newform.append(ONE_SPACE);
		    	date_count = 1;
		    }

		}

		if(isToFound == true) {
			StringBuilder finalBuilder = new StringBuilder();
			finalBuilder.append(beforeTo + ONE_SPACE + dateRange_newform.toString());
			
			dateRange_newform = finalBuilder;
		}

	//==== Reconstructing Dates ====//

		Calendar cal = Calendar.getInstance();
	    int month = cal.get(Calendar.MONTH) + 1;     
	    int day = cal.get(Calendar.DAY_OF_MONTH);
	    
		if(dateRange_newform.toString().length() == 7) {
			String startDate1 = dateRange_newform.toString().substring(0, 2);
			StringBuilder finalBuilder = new StringBuilder();
			
			if(Integer.parseInt(dateRange_newform.toString().substring(5,7)) < (month)) {
				dateRange_newform.append(YEAR_2014); 
				
				String startDate2 = dateRange_newform.toString().substring(5, 11);
				String endDate1 = dateRange_newform.toString().substring(3, 11);

				finalBuilder.append(startDate1 + startDate2 + ONE_SPACE + endDate1);		}
			
			else if(Integer.parseInt(dateRange_newform.toString().substring(5,7)) == (month)) {
				if(Integer.parseInt(dateRange_newform.toString().substring(3,5)) < day)
					dateRange_newform.append(YEAR_2014);
				else
					dateRange_newform.append(YEAR_2013);
				
				String startDate2 = dateRange_newform.toString().substring(5, 11);
				String endDate1 = dateRange_newform.toString().substring(3, 11);
				
				if(Integer.parseInt(dateRange_newform.toString().substring(0,2)) < day) 
					finalBuilder.append(startDate1 + startDate2.substring(0,2) + YEAR_2013 + ONE_SPACE + endDate1); 
				else				
					finalBuilder.append(startDate1 + startDate2 + ONE_SPACE + endDate1);		}
			
			else {
				dateRange_newform.append(YEAR_2013);
				String startDate2 = dateRange_newform.toString().substring(5, 11);
				String endDate1 = dateRange_newform.toString().substring(3, 11);

				finalBuilder.append(startDate1 + startDate2 + ONE_SPACE + endDate1);		}
			
			dateRange_newform = finalBuilder;			
		}

		else if(date_count <= DATE_MONTH_YEAR_COMPLETED && dateRange_newform.toString().length() < ONLY_ONE_DATE) {
			if(Integer.parseInt(dateRange_newform.toString().substring(2,4)) < (month)) 
				dateRange_newform.append(YEAR_2014); 

			else if(Integer.parseInt(dateRange_newform.toString().substring(2,4)) == (month)) {
				if(Integer.parseInt(dateRange_newform.toString().substring(0,2)) < day)
					dateRange_newform.append(YEAR_2014);
				else
					dateRange_newform.append(YEAR_2013);	}
			else
				dateRange_newform.append(YEAR_2013);	}

		else if(date_count <= DATE_MONTH_YEAR_COMPLETED && dateRange_newform.toString().length() > 11) {
			if(Integer.parseInt(dateRange_newform.toString().substring(11,13)) < (month)) 
				dateRange_newform.append(YEAR_2014); 

			else if(Integer.parseInt(dateRange_newform.toString().substring(11,13)) == (month)) {
				if(Integer.parseInt(dateRange_newform.toString().substring(9,11)) < day)
					dateRange_newform.append(YEAR_2014);
				else
					dateRange_newform.append(YEAR_2013);	}
			else
				dateRange_newform.append(YEAR_2013);	}

		else if(date_count <= DATE_MONTH_YEAR_COMPLETED && dateRange_newform.toString().length() == 11) {
			String startDate1 = dateRange_newform.toString().substring(0,2);
			String startDate2 = dateRange_newform.toString().substring(2,4);
			String endDate1 = dateRange_newform.toString().substring(6,8);
			String endDate2 = dateRange_newform.toString().substring(9,11);
			StringBuilder finalBuilder = new StringBuilder();

		//==== Rebuild first date ====//

			if(Integer.parseInt(startDate2) < (month)) 
				finalBuilder.append(startDate1 + startDate2 + YEAR_2014 + ONE_SPACE);

			else if(Integer.parseInt(startDate2) == (month)) {
				if(Integer.parseInt(startDate1) < day)
					finalBuilder.append(startDate1 + startDate2 + YEAR_2014 + ONE_SPACE);
				else
					finalBuilder.append(startDate1 + startDate2 + YEAR_2013 + ONE_SPACE);	}

			else if(Integer.parseInt(startDate2) > (month))
				finalBuilder.append(startDate1 + startDate2 + YEAR_2013 + ONE_SPACE);
		
		//==== Rebuild second date ====//

			if(Integer.parseInt(endDate2) < (month)) 
				finalBuilder.append(endDate1 + endDate2 + YEAR_2014);

			else if(Integer.parseInt(endDate2) == (month)) {
				if(Integer.parseInt(endDate1) < day)
					finalBuilder.append(endDate1 + endDate2 + YEAR_2014);
				else
					finalBuilder.append(endDate1 + endDate2 + YEAR_2013);	}
			else if(Integer.parseInt(endDate2) > (month))
				finalBuilder.append(endDate1 + endDate2 + YEAR_2013);
			
			dateRange_newform = finalBuilder;
		}
		
		DateRange rangeDate;
		String[] datesDeterminer = dateRange_newform.toString().split(BY_SPACE_DASH);
		if(datesDeterminer.length >= TWO_DATES) {
			if(Integer.parseInt(datesDeterminer[SECOND_DATE]) < Integer.parseInt(datesDeterminer[FIRST_DATE])) {		//Comparing both dates
				String temp = datesDeterminer[SECOND_DATE];
				datesDeterminer[SECOND_DATE] = datesDeterminer[FIRST_DATE];
				datesDeterminer[FIRST_DATE] = temp;			}

			Date startDate = new Date();
			Date endDate = new Date();	
			startDate.setDay(Integer.parseInt(datesDeterminer[FIRST_DATE].substring(0,2)));
			startDate.setMonth(Integer.parseInt(datesDeterminer[FIRST_DATE].substring(2,4)));
			startDate.setYear(Integer.parseInt(datesDeterminer[FIRST_DATE].substring(4,8)));
			endDate.setDay(Integer.parseInt(datesDeterminer[SECOND_DATE].substring(0,2)));
			endDate.setMonth(Integer.parseInt(datesDeterminer[SECOND_DATE].substring(2,4)));
			endDate.setYear(Integer.parseInt(datesDeterminer[SECOND_DATE].substring(4,8)));	
			rangeDate = new DateRange(startDate, endDate);										}

		else {
			Date endDate = new Date(); 
			endDate.setDay(Integer.parseInt(datesDeterminer[FIRST_DATE].substring(0,2)));
			endDate.setMonth(Integer.parseInt(datesDeterminer[FIRST_DATE].substring(2,4)));
			endDate.setYear(Integer.parseInt(datesDeterminer[FIRST_DATE].substring(4,8)));
			rangeDate = new DateRange(new Date(), endDate);										}

		return rangeDate;
	}

	private TimeRange generateTimeRange(String timeRange) throws NumberFormatException, Exception {
		StringBuilder timeRange_newform = new StringBuilder();

		Pattern stringPattern = Pattern.compile(REGEX_TIME, Pattern.CASE_INSENSITIVE);
		Matcher stringMatcher = stringPattern.matcher(timeRange);

		while(stringMatcher.find()) {
			String current = stringMatcher.group().trim();
			int time = 0;

			if(current.contains(PM)) {
				current = current.replace(PM, BLANK_SPACE);

				if(current.length() == SINGLE_DIGIT) {
					time = Integer.parseInt(current) + PM_12;
					timeRange_newform.append(time + DIGIT_00); }

				else if (current.length() == DOUBLE_DIGIT) {
					if(Integer.parseInt(current) < PM_12) {
						time = Integer.parseInt(current) + PM_12;
						timeRange_newform.append(time + DIGIT_00);	}
					else
						timeRange_newform.append(current + DIGIT_00);	 }

				else if (current.contains(COLON)) {
					current = current.replace(COLON, BLANK_SPACE);
					if(Integer.parseInt(current) < HRS_1200 ) {
						time = Integer.parseInt(current) + HRS_1200;
						timeRange_newform.append(time); } 
					else
						timeRange_newform.append(current);	}
																		}
			else if(current.contains(AM)) {
				current = current.replace(AM, BLANK_SPACE);

				if(current.length() == SINGLE_DIGIT)
					timeRange_newform.append(DIGIT_0 + current + DIGIT_00);

				else if (current.length() == DOUBLE_DIGIT) {
					if(Integer.parseInt(current) == PM_12)
						timeRange_newform.append(DIGIT_0000);
					else
						timeRange_newform.append(current + DIGIT_00); }

				else if (current.contains(COLON)) {
					current = current.replace(COLON, BLANK_SPACE);
					if(Integer.parseInt(current) > STARTING_FROM_12PM && Integer.parseInt(current) < JUST_BEFORE_1PM )
						timeRange_newform.append(DIGIT_00 + current.substring(2,4));
					else
						timeRange_newform.append(current);	} 
																		}
			else if(current.contains(COLON)) {
				current = current.replace(COLON, BLANK_SPACE);

				if(current.length() == TRIPLE_DIGIT)
					timeRange_newform.append(DIGIT_0 + current);
				else
					timeRange_newform.append(current);	
																		}
			else {

				if(current.length() == SINGLE_DIGIT)
					timeRange_newform.append(DIGIT_0 + current + DIGIT_00);
				else if(current.length() == DOUBLE_DIGIT)
					timeRange_newform.append(current + DIGIT_00);
				else if(current.length()==TRIPLE_DIGIT)
					timeRange_newform.append(DIGIT_0 + current);
				else
					timeRange_newform.append(current);			}

			timeRange_newform.append(ONE_SPACE);
		}

		TimeRange rangeTime;
		String[] timesDeterminer = timeRange_newform.toString().split(BY_SPACE_DASH);

		if(timesDeterminer.length == TWO_TIMES) {
			if(Integer.parseInt(timesDeterminer[SECOND_TIME]) < Integer.parseInt(timesDeterminer[FIRST_TIME])) {		//Comparing both times
				String temp = timesDeterminer[SECOND_TIME];
				timesDeterminer[SECOND_TIME] = timesDeterminer[FIRST_TIME];
				timesDeterminer[FIRST_TIME] = temp;			}

			Time startTime = new Time();
			Time endTime = new Time();
			startTime.setHour(Integer.parseInt(timesDeterminer[FIRST_TIME].substring(0,2)));
			startTime.setMinute(Integer.parseInt(timesDeterminer[FIRST_TIME].substring(2,4)));
			endTime.setHour(Integer.parseInt(timesDeterminer[SECOND_TIME].substring(0,2)));
			endTime.setMinute(Integer.parseInt(timesDeterminer[SECOND_TIME].substring(2,4)));
			rangeTime = new TimeRange(startTime, endTime);	}

		else {
			Time endTime = new Time();
			endTime.setHour(Integer.parseInt(timesDeterminer[FIRST_TIME].substring(0,2)));
			endTime.setMinute(Integer.parseInt(timesDeterminer[FIRST_TIME].substring(2,4)));
			rangeTime = new TimeRange(new Time(), endTime);
		}

		return rangeTime;
	}

//==========================================================================//
//============================ Auxiliary Methods ===========================//
//==========================================================================//

	private int getMonth(String currMonth) {
		switch(currMonth.toUpperCase()) {
			case "JAN": case "JANUARY":
				return 1;
			case "FEB": case "FEBRUARY":
				return 2;
			case "MAR": case "MARCH":
				return 3;
			case "APR": case "APRIL":
				return 4;
			case "MAY": 
				return 5;
			case "JUN": case "JUNE":
				return 6;
			case "JUL": case "JULY":
				return 7;
			case "AUG": case AUGUST_STRING:
				return 8;
			case "SEP": case "SEPT": case "SEPTEMBER":
				return 9;
			case "OCT": case "OCTOBER":
				return 10;
			case "NOV": case "NOVEMBER":
				return 11;
			case "DEC": case "DECEMBER":
				return 12;
			default:
				return 0;
		}
	}

	private boolean isInteger(String str) {
    	try {
        	Integer.parseInt(str);
        	return true;
    	} catch (NumberFormatException nfe) {
        	return false;
        }
    }

    private boolean isAlpha(String str) {
    	char[] chars = str.toCharArray();

    	for (char c : chars) {
        	if(!Character.isLetter(c)) 
          	  	return false;
    	}

    	return true;
	}

	private boolean isMonth(String str) {
		return str.matches(REGEX_MONTHS);
	}
	
	private boolean isDayOfWeek(String str) {
		return str.matches(REGEX_DAYS_OF_WEEK);
	}

}